/*
 *  FILE: vfs_syscall.c
 *  AUTH: mcc | jal
 *  DESC:
 *  DATE: Wed Apr  8 02:46:19 1998
 *  $Id: vfs_syscall.c,v 1.1 2012/10/10 20:06:46 william Exp $
 */

#include "kernel.h"
#include "errno.h"
#include "globals.h"
#include "fs/vfs.h"
#include "fs/file.h"
#include "fs/vnode.h"
#include "fs/vfs_syscall.h"
#include "fs/open.h"
#include "fs/fcntl.h"
#include "fs/lseek.h"
#include "mm/kmalloc.h"
#include "util/string.h"
#include "util/printf.h"
#include "fs/stat.h"
#include "util/debug.h"

/* To read a file:
 *      o fget(fd)
 *      o call its virtual read f_op
 *      o update f_pos
 *      o fput() it
 *      o return the number of bytes read, or an error
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd is not a valid file descriptor or is not open for reading.
 *      o EISDIR
 *        fd refers to a directory.
 *
 * In all cases, be sure you do not leak file refcounts by returning before
 * you fput() a file that you fget()'ed.
 */
int do_read(int fd, void *buf, size_t nbytes) {
	dbg(DBG_VFS,"Enter do_read()\n");
	/*return error应该return什么？？-1?*/
	dbg(DBG_VFS,"try to read fd %d\n",fd);
	if (fd < 0 || fd >= NFILES) {
		dbg(DBG_VFS,"invalid fd\n");
		return -EBADF;
	}
	file_t *f = fget(fd);
	/* dbg(DBG_VFS,"try to read fd %d(fmode=%d)\n",fd,f->f_mode); */
	if (f == NULL) {/*fget()返回NULL时还用不用put*/
		dbg(DBG_VFS,"fd %d is not associated with any file\n",fd);
		return -EBADF;
	}
	if (S_ISDIR(f->f_vnode->vn_mode) == 1) {/*read == NULL是怎么回事？*/
		fput(f);
		dbg(DBG_VFS,"fd %d is associated with a dir\n",fd);
		return -EISDIR;
	} else if ((f->f_mode&FMODE_READ) != 1) {
		fput(f);
		dbg(DBG_VFS,"fd %d is not open for read\n",fd);
		return -EBADF;
	}
/*	if (f->f_pos >= f->f_vnode->vn_len){*/ /*the end of file has been reached,error*/
/*		return -EBADF;
	} */else {
		size_t bytes_read = f->f_vnode->vn_ops->read(f->f_vnode, f->f_pos, buf, nbytes);
		if (bytes_read < nbytes) {
			do_lseek(fd, 0, SEEK_END);
			fput(f);
			dbg(DBG_VFS,"read %d from fd %d\n",bytes_read,fd);
			return bytes_read;
		} else {
			do_lseek(fd, bytes_read, SEEK_CUR);
			fput(f);
			dbg(DBG_VFS,"read %d from fd %d\n",bytes_read,fd);
			return bytes_read;
		}
	}

	/*        NOT_YET_IMPLEMENTED("VFS: do_read");*/
}

/* Very similar to do_read.  Check f_mode to be sure the file is writable.  If
 * f_mode & FMODE_APPEND, do_lseek() to the end of the file, call the write
 * f_op, and fput the file.  As always, be mindful of refcount leaks.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd is not a valid file descriptor or is not open for writing.
 */
int do_write(int fd, const void *buf, size_t nbytes) {
	dbg(DBG_VFS,"Enter do_write()\n");
	int bytes_write;
	if (fd < 0 || fd >= NFILES) {
		dbg(DBG_VFS,"invalid fd %d:\n",fd);
		return -EBADF;
	}
	file_t *f = fget(fd);
	if (f == NULL) {
		dbg(DBG_VFS,"fd %d points to null\n",fd);
		return -EBADF;
	}
	if (S_ISDIR(f->f_vnode->vn_mode) == 1) {/*read == NULL是怎么回事？*/
		dbg(DBG_VFS,"fd %d points to a directory\n",fd);
		fput(f);
		return -EISDIR;
	} else {

		if (((f->f_mode&FMODE_WRITE) == 0) && ((f->f_mode & FMODE_APPEND) == 0)) {
			dbg(DBG_VFS,"fd %d is not open for write",fd);
			fput(f);
			return -EBADF;
		} else {
			if ((f->f_mode & FMODE_APPEND)!=0) {
				dbg(DBG_VFS,"fmode %d\n",f->f_mode);
				do_lseek(fd, 0, SEEK_END);
				dbg(DBG_VFS,"current position %d\n",f->f_pos);
				bytes_write = f->f_vnode->vn_ops->write(f->f_vnode, f->f_pos,
						buf, nbytes);
				do_lseek(fd, 0, SEEK_END);
				fput(f);
				dbg(DBG_VFS,"append %d bytes to fd %d, current position %d\n",bytes_write, fd,f->f_pos);
			}
			else if ((f->f_mode & FMODE_WRITE)!=0) {
				dbg(DBG_VFS,"current position %d\n",f->f_pos);
				bytes_write = f->f_vnode->vn_ops->write(f->f_vnode, f->f_pos,
						buf, nbytes);
				do_lseek(fd, bytes_write, SEEK_CUR);
				fput(f);
				dbg(DBG_VFS,"write %d bytes to fd %d, current position %d\n",bytes_write, fd,f->f_pos);
			}

			KASSERT((S_ISCHR(f->f_vnode->vn_mode)) ||(S_ISBLK(f->f_vnode->vn_mode)) ||((S_ISREG(f->f_vnode->vn_mode)) && (f->f_pos <= f->f_vnode->vn_len)));
			dbg(DBG_VFS,"(GRADING 3.a):write successful\n");

		}

	}
	return bytes_write;
	/*        NOT_YET_IMPLEMENTED("VFS: do_write");*/
}

/*
 * Zero curproc->p_files[fd], and fput() the file. Return 0 on success
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd isn't a valid open file descriptor.
 */
int do_close(int fd) {
	dbg(DBG_VFS,"Enter do_close()\n");
	if (fd < 0 || fd >= NFILES) {
		dbg(DBG_VFS,"invalid fd %d:\n",fd);
		return -EBADF;
	}
	if (curproc->p_files[fd] == NULL) {
		dbg(DBG_VFS,"fd %d points to null\n",fd);
		return -EBADF;/*如果本来就是NULL，应该返回什么？*/
	} else {
		
		fput(curproc->p_files[fd]);
		curproc->p_files[fd] = NULL;
		dbg(DBG_VFS,"fd %d closed\n",fd);
	}

	/*       NOT_YET_IMPLEMENTED("VFS: do_close");*/
	return 0;
}

/* To dup a file:
 *      o fget(fd) to up fd's refcount
 *      o get_empty_fd()
 *      o point the new fd to the same file_t* as the given fd
 *      o return the new file descriptor
 *
 * Don't fput() the fd unless something goes wrong.  Since we are creating
 * another reference to the file_t*, we want to up the refcount.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd isn't an open file descriptor.
 *      o EMFILE
 *        The process already has the maximum number of file descriptors open
 *        and tried to open a new one.
 */
int do_dup(int fd) {
	dbg(DBG_VFS,"Enter do_dup()\n");
	int new_fd;
	if (fd < 0 || fd >= NFILES) {
		dbg(DBG_VFS,"invalid fd %d:\n",fd);
		return -EBADF;
	}
	file_t *f = fget(fd);
	new_fd = get_empty_fd(curproc);

	if (new_fd == EMFILE) {
		dbg(DBG_VFS,"The process already has the maximum number of file descriptors open\n");
		fput(f);
		return EMFILE;
	} else {
		if (f == NULL) {
			dbg(DBG_VFS,"fd %d points to null\n",fd);
			return -EBADF;
		}
		dbg(DBG_VFS,"newfd %d duped\n",new_fd);
		curproc->p_files[new_fd] = f;
	}

	
	return new_fd;
}

/* Same as do_dup, but insted of using get_empty_fd() to get the new fd,
 * they give it to us in 'nfd'.  If nfd is in use (and not the same as ofd)
 * do_close() it first.  Then return the new file descriptor.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        ofd isn't an open file descriptor, or nfd is out of the allowed
 *        range for file descriptors.
 */
int do_dup2(int ofd, int nfd) {
	

	dbg(DBG_VFS,"Enter do_dup2()\n");
	if (ofd < 0 || ofd >= NFILES || nfd < 0 || nfd >= NFILES) {
		dbg(DBG_VFS,"invalid fd:ofd=%d,nfd=%d\n",ofd,nfd);
		return -EBADF;
	}
	
	file_t *of = fget(ofd);

	if (of == NULL) {
		dbg(DBG_VFS,"ofd %d points to null\n",ofd);
		return -EBADF;
	} else {
		file_t *nf = fget(nfd);
		if (nf == of) {
			fput(nf);
			fput(of);
			dbg(DBG_VFS,"ofd %d and nfd %d points to same file\n",ofd, nfd);
			return nfd;
		} else if (nf != NULL && nf != of) {
			dbg(DBG_VFS,"nfd %d points to another file\n",nfd);
			fput(nf);
			do_close(nfd);
		}
		curproc->p_files[nfd] = of;
		dbg(DBG_VFS,"dup2 successful\n");
	}
	
	return nfd;
}

/*
 * This routine creates a special file of the type specified by 'mode' at
 * the location specified by 'path'. 'mode' should be one of S_IFCHR or
 * S_IFBLK (you might note that mknod(2) normally allows one to create
 * regular files as well-- for simplicity this is not the case in Weenix).
 * 'devid', as you might expect, is the device identifier of the device
 * that the new special file should represent.
 *
 * You might use a combination of dir_namev, lookup, and the fs-specific
 * mknod (that is, the containing directory's 'mknod' vnode operation).
 * Return the result of the fs-specific mknod, or an error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        mode requested creation of something other than a device special
 *        file.
 *      o EEXIST
 *        path already exists.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int do_mknod(const char *path, int mode, unsigned devid)
 {
	dbg(DBG_VFS,"Enter do_mknod()\n");
	if (mode!=S_IFCHR && mode!=S_IFBLK)
		return -EINVAL;
	size_t namelen;
	const char *name;
	vnode_t *res_vnode, *result;
	int error, ret;
	if ((error = dir_namev(path, &namelen, &name, NULL, &res_vnode)) < 0)
		return error;
	error = lookup(res_vnode, name, namelen, &result);
	if (error == -ENOENT) 
	{
		KASSERT(NULL != res_vnode->vn_ops->mknod);
		dbg(DBG_VFS,"(Grading 3.b) The mknod pointer to the corresponding vnode is not null.\n");
		ret = res_vnode->vn_ops->mknod(res_vnode, name, namelen, mode,(devid_t) devid);
		vput(res_vnode);
		return ret;	
	}
	else if (error >= 0)
	{
		vput(res_vnode);
		vput(result);
		return -EEXIST;
	}
	else
	{
		vput(res_vnode);
		return error;
	}
	
	/*NOT_YET_IMPLEMENTED("VFS: do_mknod");*/
	
}

/* Use dir_namev() to find the vnode of the dir we want to make the new
 * directory in.  Then use lookup() to make sure it doesn't already exist.
 * Finally call the dir's mkdir vn_ops. Return what it returns.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EEXIST
 *        path already exists.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int do_mkdir(const char *path)
{
	dbg(DBG_VFS,"Enter do_mkdir()\n");
	size_t namelen;
	const char *name;
	vnode_t *res_vnode, *result;
	int error, ret;
	if ((error = dir_namev(path, &namelen, &name, NULL, &res_vnode)) < 0)
		return error;
	error = lookup(res_vnode, name, namelen, &result);

	if (error == -ENOENT) {
		KASSERT(NULL != res_vnode->vn_ops->mkdir);
		dbg(DBG_VFS,"(Grading 3.c) The mkdir pointer to the corresponding vnode is not null.\n");
		ret = res_vnode->vn_ops->mkdir(res_vnode, name, namelen);
		vput(res_vnode);
		return ret;
		
	} 
	else if (error >= 0)
	{
		vput(res_vnode);
		vput(result);
		return -EEXIST;
	}
	else
	{
		vput(res_vnode);
		return error;
	}
	
	
	/*NOT_YET_IMPLEMENTED("VFS: do_mkdir");*/
}

/* Use dir_namev() to find the vnode of the directory containing the dir to be
 * removed. Then call the containing dir's rmdir v_op.  The rmdir v_op will
 * return an error if the dir to be removed does not exist or is not empty, so
 * you don't need to worry about that here. Return the value of the v_op,
 * or an error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        path has "." as its final component.
 *      o ENOTEMPTY
 *        path has ".." as its final component.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int do_rmdir(const char *path)
 {
	dbg(DBG_VFS,"Enter do_rmdir()\n");
	size_t namelen;
	const char *name;
	vnode_t *res_vnode;
	int error, ret, len;
	for(len=0;path[len]!='\0';len++);
	
	if ((error = dir_namev(path, &namelen, &name, NULL, &res_vnode)) < 0)
	{
	dbg(DBG_VFS,"do_rmdir path=%s, err=%d\n", path, error);	
		if(error!=-ENOENT)
		{	if(path[len-1]=='.'&&path[len-2]=='.')	
				return -ENOTEMPTY;
			else if(path[len-1]=='.')
				return -EINVAL;
		}
		return error;
	}
	if(path[len-1]=='.'&&path[len-2]=='.')	
	{
		vput(res_vnode);
		return -ENOTEMPTY;
	}
	else if(path[len-1]=='.')
	{
		vput(res_vnode);
		return -EINVAL;
	}
	KASSERT(NULL != res_vnode->vn_ops->rmdir);
	dbg(DBG_VFS,"(Grading 3.d) The rmdir pointer to the corresponding vnode is not null.\n");
	ret = res_vnode->vn_ops->rmdir(res_vnode, name, namelen);
	vput(res_vnode);
	return ret;

	/*NOT_YET_IMPLEMENTED("VFS: do_rmdir");
	 return -1;*/
}

/*
 * Same as do_rmdir, but for files.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EISDIR
 *        path refers to a directory.
 *      o ENOENT
 *        A component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int do_unlink(const char *path) {
	dbg(DBG_VFS,"Enter do_unlink()\n");
	size_t namelen;
	const char *name;
	vnode_t *res_vnode, *result;
	int error, ret;
	if ((error = dir_namev(path, &namelen, &name, NULL, &res_vnode)) < 0)
		return error;
	if ((error = lookup(res_vnode, name, namelen, &result)) < 0)
	{
		vput(res_vnode);
		return error;
	}
	if (S_ISDIR(result->vn_mode))
	{
		vput(res_vnode);
		vput(result);
		return -EISDIR;
	}
	KASSERT(NULL != res_vnode->vn_ops->unlink);
	dbg(DBG_VFS,"(Grading 3.e) The unlink pointer to the corresponding vnode is not null.\n");
	ret = res_vnode->vn_ops->unlink(res_vnode, name, namelen);
	vput(res_vnode);
	/*while(result->vn_refcount!=0)*/
		vput(result);
	return ret;
	/*NOT_YET_IMPLEMENTED("VFS: do_unlink");
	 return -1;*/
}

/* To link:
 *      o open_namev(from)
 *      o dir_namev(to)
 *      o call the destination dir's (to) link vn_ops.
 *      o return the result of link, or an error
 *
 * Remember to vput the vnodes returned from open_namev and dir_namev.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EEXIST
 *        to already exists.
 *      o ENOENT
 *        A directory component in from or to does not exist.
 *      o ENOTDIR
 *        A component used as a directory in from or to is not, in fact, a
 *        directory.
 *      o ENAMETOOLONG
 *        A component of from or to was too long.
 */
int do_link(const char *from, const char *to) {
	dbg(DBG_VFS,"Enter do_link()\n");
	int flag, err1, err2, error, ret;
	size_t namelen;
	const char *name;
	vnode_t *result, *res_vnode, *base, *dup;

	if ((err1 = open_namev(from, 0, &res_vnode, NULL)) < 0)
		return err1;

	if ((err2 = dir_namev(to, &namelen, &name, NULL, &result)) < 0) {
		vput(res_vnode);
		return err2;
	}
	error = lookup(result, name, namelen, &dup);
	if (error == -ENOENT) {
		ret = result->vn_ops->link(res_vnode, result, name, namelen);
		vput(res_vnode);
		vput(result);
		return ret;
	} else if (error >= 0) {
		vput(res_vnode);
		vput(result);
		vput(dup);
		return -EEXIST;
	} else {
		vput(res_vnode);
		vput(result);
		return error;
	}
	/*NOT_YET_IMPLEMENTED("VFS: do_link");
	 return -1;*/
}

/*      o link newname to oldname
 *      o unlink oldname
 *      o return the value of unlink, or an error
 *
 * Note that this does not provide the same behavior as the
 * Linux system call (if unlink fails then two links to the
 * file could exist).
 */
int do_rename(const char *oldname, const char *newname) {
	int retv;
	retv = do_link(oldname,newname);
	if (retv != 0) {
		dbg(DBG_ERROR,"do_rename() err: in do_link returned error.\n");
	} else
		dbg(DBG_VFS,"VFS do_rename() successful\n");
	return do_unlink(oldname);
	/*NOT_YET_IMPLEMENTED("VFS: do_rename");
	 return -1;*/
}

/* Make the named directory the current process's cwd (current working
 * directory).  Don't forget to down the refcount to the old cwd (vput()) and
 * up the refcount to the new cwd (open_namev() or vget()). Return 0 on
 * success.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o ENOENT
 *        path does not exist.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 *      o ENOTDIR
 *        A component of path is not a directory.
 */
int do_chdir(const char *path) {
	dbg(DBG_VFS,"Enter do_chdir()\n");
	int retv;
	vnode_t *node;
	/*curproc->p_cwd;*/
	retv=open_namev(path,0,&node,NULL);
	if (retv<0)
	{
		/*deal with ENAMETOOLONG and ENOENT*/
		dbg(DBG_ERROR,"do_chdir err: open_namev error.\n");
		return retv;
	}
	/*deal with ENOTDIR*/
	if(!S_ISDIR(node->vn_mode))
	{
		vput(node);
		return -ENOTDIR;
	}
	
	/*down the refcount to the old cwd */
	vput(curproc->p_cwd);
	/*update cwd*/
	curproc->p_cwd = node;
	return 0;

	/*NOT_YET_IMPLEMENTED("VFS: do_chdir");
	return -1;*/
}

/* Call the readdir f_op on the given fd, filling in the given dirent_t*.
 * If the readdir f_op is successful, it will return a positive value which
 * is the number of bytes copied to the dirent_t.  You need to increment the
 * file_t's f_pos by this amount.  As always, be aware of refcounts, check
 * the return value of the fget and the virtual function, and be sure the
 * virtual function exists (is not null) before calling it.
 *
 * Return either 0 or sizeof(dirent_t), or -errno.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        Invalid file descriptor fd.
 *      o ENOTDIR
 *        File descriptor does not refer to a directory.
 */
int do_getdent(int fd, struct dirent *dirp) {
	dbg(DBG_VFS,"Enter do_getdent()\n");
	file_t *f_node;
	int bytes;
	if (fd<0 || fd> NFILES){
		dbg(DBG_ERROR,"do_getdent() err: fd < 0 or fd > NFILES.\n");
		return -EBADF;
	}

	f_node=fget(fd);

	if (f_node==NULL){
		dbg(DBG_ERROR,"do_getdent() err: fd==NULL.\n");
		return -EBADF;
	}
	if (!S_ISDIR(f_node->f_vnode->vn_mode)||(f_node->f_vnode->vn_ops->readdir==NULL)){
		fput(f_node);/*decrement ref*/
		dbg(DBG_ERROR,"do_getdent() err: File descriptor does not refer to a directory.\n");
		return -ENOTDIR;
	}

	bytes=f_node->f_vnode->vn_ops->readdir(f_node->f_vnode,f_node->f_pos,dirp);

	f_node->f_pos=f_node->f_pos+bytes;

	fput(f_node);
	/* if byte==0, reaches the end of directory*/
	if (bytes==0){
		do_lseek(fd,0,SEEK_END);
		return 0;
	}
	else{
		return sizeof(dirent_t);
	}



	/*NOT_YET_IMPLEMENTED("VFS: do_getdent");
	return -1;*/
}

/*
 * Modify f_pos according to offset and whence.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd is not an open file descriptor.
 *      o EINVAL
 *        whence is not one of SEEK_SET, SEEK_CUR, SEEK_END; or the resulting
 *        file offset would be negative.
 */
int do_lseek(int fd, int offset, int whence) {
	dbg(DBG_VFS,"Enter do_lseek()\n");
	file_t *f;
	/*int ret;*/
	if (fd<0){
		dbg(DBG_ERROR,"do_lseek() err: due to fd is not an open file descriptor.\n");
				return -EBADF;
	}
	

	if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
			dbg(DBG_ERROR,"do_lseek() err: due to invalid 3rd argument.\n");
			return -EINVAL;
		}
	f = fget(fd);
	if (f == NULL) {
		dbg(DBG_ERROR,"do_lseek() err: due to fd is not an open file descriptor.\n");
		return -EBADF;
	}
	


	switch (whence) {
	/*
	 *  description:
	 * o  If whence is SEEK_SET, the pointer  is  set  to  offset
          bytes.
       o  If whence is  SEEK_CUR,  the  pointer  is  set  to  its
          current location plus offset.

       o  If whence is SEEK_END, the pointer is set to  the  size
          of the file plus offset.
	 */
	case SEEK_SET:
		if (offset<0)
		{
			fput(f);
			return -EINVAL;
		}

		f->f_pos = offset;
		break;

	case SEEK_CUR:
		if (f->f_pos+offset<0)
		{
			fput(f);
			return -EINVAL;
		}
		dbg(DBG_TESTPASS,"do_lseek() mode = SEEK_CUR, cur pos: %d, offset: %d.\n",f->f_pos,offset);
		f->f_pos = f->f_pos + offset;
		break;

	case SEEK_END:
		if (f->f_vnode->vn_len + offset<0)
		{
			fput(f);
			return -EINVAL;
		}
		dbg(DBG_TESTPASS,"do_lseek() mode = SEEK_END, end pos: %d, offset: %d.\n",f->f_vnode->vn_len,offset);
		f->f_pos = f->f_vnode->vn_len + offset;
		break;

	}
	
	if (f->f_pos<0)
	{
		/*f->f_pos=0;*/
		dbg(DBG_ERROR,"should not get to here.\n");
		fput(f);
		return -EINVAL;
	}

	fput(f);/*decrements the reference count by fget()*/
	dbg(DBG_VFS,"do_lseek() returns successful\n");
	return f->f_pos;

	/*NOT_YET_IMPLEMENTED("VFS: do_lseek");
	 return -1;*/
}

/*
 * Find the vnode associated with the path, and call the stat() vnode operation.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o ENOENT
 *        A component of path does not exist.
 *      o ENOTDIR
 *        A component of the path prefix of path is not a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int do_stat(const char *path, struct stat *buf) {
	dbg(DBG_VFS,"Enter do_stat()\n");
	int retv;
	vnode_t *node;
	vnode_t *result;
	size_t len;
	const char *name;

	if( strlen(path) > MAXPATHLEN){
		dbg(DBG_ERROR,"do_stat() err: due to A component of path was too long.\n");
		return -ENAMETOOLONG;
	}
	
	/*其实这些errcode dir_namev都能管，但是题目要求是在VFS level handle*/
	/*retv = dir_namev(path,&len,&name,NULL,&node);*/
	retv=open_namev(path,0,&node,NULL);
	if ( retv < 0 ){
		dbg(DBG_ERROR,"do_stat() err: check errcode\n");
		return retv;
	}
	/*retv=lookup(node,name,len,&result);
	if (retv<0){
		dbg(DBG_ERROR,"do_stat() err: check errcode");
		return retv;
	}*/
	/*decrement node 
	vput(node);*/
	KASSERT(NULL != node->vn_ops->stat);
	dbg(DBG_VFS,"(GRADING 3.f):node->vn_ops->stat is not NULL\n");
	/*call the stat() vnode operation*/
	retv=node->vn_ops->stat(node,buf);
	vput(node);
	dbg(DBG_VFS,"Leave do_stat()\n");
	return retv;
	


	/*NOT_YET_IMPLEMENTED("VFS: do_stat");
	return -1;*/
}

#ifdef __MOUNTING__
/*
 * Implementing this function is not required and strongly discouraged unless
 * you are absolutely sure your Weenix is perfect.
 *
 * This is the syscall entry point into vfs for mounting. You will need to
 * create the fs_t struct and populate its fs_dev and fs_type fields before
 * calling vfs's mountfunc(). mountfunc() will use the fields you populated
 * in order to determine which underlying filesystem's mount function should
 * be run, then it will finish setting up the fs_t struct. At this point you
 * have a fully functioning file system, however it is not mounted on the
 * virtual file system, you will need to call vfs_mount to do this.
 *
 * There are lots of things which can go wrong here. Make sure you have good
 * error handling. Remember the fs_dev and fs_type buffers have limited size
 * so you should not write arbitrary length strings to them.
 */
int
do_mount(const char *source, const char *target, const char *type)
{
	NOT_YET_IMPLEMENTED("MOUNTING: do_mount");
	return -EINVAL;
}

/*
 * Implementing this function is not required and strongly discouraged unless
 * you are absolutley sure your Weenix is perfect.
 *
 * This function delegates all of the real work to vfs_umount. You should not worry
 * about freeing the fs_t struct here, that is done in vfs_umount. All this function
 * does is figure out which file system to pass to vfs_umount and do good error
 * checking.
 */
int
do_umount(const char *target)
{
	NOT_YET_IMPLEMENTED("MOUNTING: do_umount");
	return -EINVAL;
}
#endif
